#pragma once
#include"mgepch.h"


namespace myMiniEngine {

	enum class ShaderDataType {
		None=0,Float,Float2, Float3, Float4,Mat3,Mat4,Int,Int2,Int3,Int4,Bool
	};

	static uint32_t ShaderDataTypeSize(ShaderDataType type) {
		switch (type)
		{
		case myMiniEngine::ShaderDataType::Float: return 4;
			break;
		case myMiniEngine::ShaderDataType::Float2: return 4*2;
			break;
		case myMiniEngine::ShaderDataType::Float3:return 4 * 3;
			break;
		case myMiniEngine::ShaderDataType::Float4:return 4 * 4;
			break;
		case myMiniEngine::ShaderDataType::Mat3:return 4 * 3 * 3;
			break;
		case myMiniEngine::ShaderDataType::Mat4:return 4*4*4;
			break;
		case myMiniEngine::ShaderDataType::Int:return 4;
			break;
		case myMiniEngine::ShaderDataType::Int2:return 4*2;
			break;
		case myMiniEngine::ShaderDataType::Int3:return 4*3;
			break;
		case myMiniEngine::ShaderDataType::Int4:return 4*4;
			break;
		case myMiniEngine::ShaderDataType::Bool:return 1;
			break;

		}
		MGE_CORE_ASSERT(flase, "Unknown type");
		return 0;
	}

	struct BufferElement {
		std::string Name;
		ShaderDataType Type;
		uint32_t Offset;
		uint32_t Size;
		bool Normalized;
		
		BufferElement(ShaderDataType type, const std::string& name,bool normalized=false ) : Name(name), Type(type), Size(ShaderDataTypeSize(type))
			, Offset(0),Normalized(normalized) {}

		uint32_t GetComponentCount() const {
			switch (Type)
			{
			case myMiniEngine::ShaderDataType::Float: return 1;
			case myMiniEngine::ShaderDataType::Float2:return 2;
			case myMiniEngine::ShaderDataType::Float3:return 3;
			case myMiniEngine::ShaderDataType::Float4:return 4;
			case myMiniEngine::ShaderDataType::Mat3:return 3*3;
			case myMiniEngine::ShaderDataType::Mat4:return 4*4;
			case myMiniEngine::ShaderDataType::Int:return 1;
			case myMiniEngine::ShaderDataType::Int2:return 2;
			case myMiniEngine::ShaderDataType::Int3:return 3;
			case myMiniEngine::ShaderDataType::Int4:return 4;
			case myMiniEngine::ShaderDataType::Bool:return 1;
			}
		}
	};

	class BufferLayout {
	public:
		BufferLayout(){}
		BufferLayout(const std::initializer_list<BufferElement>& _elements): elements(_elements) {
			CalculateOffsetAndStride();	
		}

		inline uint32_t GetStride() const { return stride; }
		inline const std::vector<BufferElement>& GetElements()const { return elements; }

		std::vector<BufferElement>::iterator begin() { return elements.begin(); }
		std::vector<BufferElement>::iterator end() { return elements.end(); }
		std::vector<BufferElement>::const_iterator begin() const { return elements.begin(); }
		std::vector<BufferElement>::const_iterator end() const { return elements.end(); }
	private:
		std::vector<BufferElement> elements;
		uint32_t stride = 0;
		void CalculateOffsetAndStride() {
			uint32_t offset = 0;
			stride = 0;
			for (auto& ele : elements) {
				ele.Offset = offset;
				offset += ele.Size;
				stride += ele.Size;
			}
		}

	};


	class VertexBuffer
	{
	public:
		virtual ~VertexBuffer() {}

		virtual void Bind() const = 0;
		virtual void Unbind() const = 0;

		virtual const BufferLayout& GetLayout() const = 0;
		virtual void SetLayout(const BufferLayout& layout) = 0;

		static VertexBuffer* Create(float* vertices, uint32_t size);
	};

	class IndexBuffer {
	public:
		virtual ~IndexBuffer() {}

		virtual void Bind()const = 0;
		virtual void Unbind()const = 0;
		virtual uint32_t GetCount() const = 0;


		static IndexBuffer* Create(uint32_t* indices, uint32_t size);
	};
}